﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace MetaHeuristics.Aco
{
    using MetaHeuristics.Collections;
    using MetaHeuristics.Aco.Collections;
    using MetaHeuristics.Tsp;

    public class AcoForTspAlgorithm
    {
        #region Fields
        private CityDistanceMatrix distances=null;
        private PheromoneMatrix pheromones = null;
        private SymmetricFloatMatrix choiceInfos = null;
        private Ant bestAnt = null;
        private AntCollection ants=null;
        private TspProblem tsp = null;

        private ILocalSearch localSearch = null;
        private IDecisionRule decisionRule = null;
        private IPheromoneUpdater pheromoneUpdater = null;
        private ILocalPheromoneUpdater localPheromoneUpdater = null;
        private DaemonActionCollection daemons = new DaemonActionCollection();

        private int antCount = 20;

        private Random rnd = new Random((int)DateTime.Now.Ticks);
        private TimeCounter timer = new TimeCounter();
        private int iterationCount = -1;
        #endregion

        public AcoForTspAlgorithm(TspProblem tsp)
        {
            this.tsp = tsp;

            this.Daemons.Aco = this;
            this.AntCount = this.Tsp.Cities.Count;
            this.LocalSearch = new BestImprovementTwoOptLocalSearch();
            this.DecisionRule = new AsDecisionRule();
            this.PheromoneUpdater = new AsPheromoneUpdater();
            this.LocalPheromoneUpdater = new EmptyLocalPheromoneUpdater();
        }

        #region Properties
        public Ant BestAnt
        {
            get
            {
                return this.bestAnt;
            }
        }

        public DaemonActionCollection Daemons
        {
            get
            {
                return this.daemons;
            }
        }


        public TspProblem Tsp
        {
            get
            {
                return this.tsp;
            }
            set
            {
                this.tsp = value;
            }
        }

        public IDecisionRule DecisionRule
        {
            get
            {
                return this.decisionRule;
            }
            set
            {
                this.decisionRule = value;
                this.decisionRule.Aco = this;
            }
        }

        public IPheromoneUpdater PheromoneUpdater
        {
            get
            {
                return this.pheromoneUpdater;
            }
            set
            {
                this.pheromoneUpdater = value;
                this.pheromoneUpdater.Aco = this;
            }
        }

        public ILocalPheromoneUpdater LocalPheromoneUpdater
        {
            get
            {
                return this.localPheromoneUpdater;
            }
            set
            {
                this.localPheromoneUpdater = value;
                this.localPheromoneUpdater.Aco = this;
            }
        }

        public ILocalSearch LocalSearch
        {
            get
            {
                return this.localSearch;
            }
            set
            {
                this.localSearch = value;
                this.localSearch.Aco = this;
            }        
        }

        public int AntCount
        {
            get
            {
                return this.antCount;
            }
            set
            {
                this.antCount = value;
            }
        }

        public CityCollection Cities
        {
            get
            {
                return this.tsp.Cities;
            }
        }

        public AntCollection Ants
        {
            get
            {
                return this.ants;
            }
        }

        public CityDistanceMatrix Distances
        {
            get
            {
                return this.distances;
            }
        }

        public int IterationCount
        {
            get
            {
                return this.iterationCount;
            }
        }

        public TimeSpan Duration
        {
            get
            {
                return this.timer.Duration;
            }
        }

        public PheromoneMatrix Pheromones
        {
            get
            {
                return this.pheromones;
            }
        }

        public SymmetricFloatMatrix ChoiceInfos
        {
            get
            {
                return this.choiceInfos;
            }
        }
        #endregion
        #region Events
        public event EventHandler FinishedIteration;

        protected virtual void OnFinishedIteration()
        {
            if (this.FinishedIteration != null)
                this.FinishedIteration(this, EventArgs.Empty);
        }

        public event EventHandler BestAntChanged;

        protected virtual void OnBestAntChanged()
        {
            if (this.BestAntChanged != null)
                this.BestAntChanged(this, EventArgs.Empty);
        }

        #endregion

        public virtual void Solve()
        {
            this.timer.Start();

            this.InitializeData();
            while(!this.Terminate())
            {
                Iterate();
                this.OnFinishedIteration();
            }

            this.timer.Stop();
        }

        protected virtual void Iterate()
        {
            this.ConstructSolutions();
            this.LocalSearch.Search();
            this.UpdateStatistics();
            this.PheromoneUpdater.Update();
            this.Daemons.Act();
            this.Pheromones.Update();
            this.UpdateBestAnts();
        }

        #region InitializeData
        protected void InitializeData()
        {
            this.ComputeDistances();
            this.ComputeBestAnt();
            this.DecisionRule.Initialize();
            this.ComputeChoiceInformation();

            this.InitializeAnts();
            this.InitializeParameters();
            this.InitializeStatistics();
        }

        protected virtual void ComputeBestAnt()
        {
            this.bestAnt = new Ant();
            // compute tau0
            int r = rnd.Next(this.Cities.Count);
            for (int i = 0; i < this.Cities.Count; ++i)
                bestAnt.AddCity(i);
            this.BestAnt.CloseTour();
            this.BestAnt.Tours.Length = this.distances.ComputeTourLength(this.BestAnt.Tours);
        }


        protected virtual void ComputeDistances()
        {
            this.distances = new CityDistanceMatrix(Cities);
        }

        protected virtual void ComputeChoiceInformation()
        {
            float Cnn = this.BestAnt.Tours.Length;
            int m = this.AntCount;
            float tau0 = m / Cnn;

            this.pheromones = new PheromoneMatrix(Cities.Count, tau0);
            this.choiceInfos = new SymmetricFloatMatrix(Cities.Count,1);
            this.PheromoneUpdater.Initialize();
        }

        protected virtual void InitializeAnts()
        {
            this.ants = new AntCollection(this.AntCount);
            for (int i = 0; i < this.AntCount; ++i)
            {
                this.ants.Add(new Ant());
            }
        }

        protected virtual void InitializeParameters()
        {
        }

        protected virtual void InitializeStatistics()
        {
            this.iterationCount = 0;
        }

        #endregion

        protected virtual void UpdateBestAnts()
        {
            bool changed=false;
            foreach (Ant ant in this.Ants)
            {
                if (ant.Tours.Length < this.bestAnt.Tours.Length)
                {
                    this.bestAnt = ant.Clone();
                    changed = true;
                }
            }

            if (changed)
                this.OnBestAntChanged();
        }

        protected virtual bool Terminate()
        {
            
            // maximum number of iteration reached
            if (this.iterationCount >= this.tsp.MaxIteration)
                return true;

            // maximum time reached
            if (this.timer.Duration >= this.tsp.MaxDuration)
                return true;

            // optimal found
            if (this.bestAnt.Tours.Length <= this.Tsp.OptimalLength)
                return true;

            return false;
        }

        protected void ConstructSolutions()
        {
            // clear ant memory
            foreach (Ant ant in this.Ants)
            {
                ant.Clear();
            }

            // initialize first city randomly
            AssignFirstCity();

            // applying decision rule
            int step = 1;
            while (step < this.Cities.Count)
            {
                step++;

                foreach (Ant ant in this.Ants)
                {
                    this.DecisionRule.MakeDecision(ant);
                    this.LocalPheromoneUpdater.Update(ant);
                }
            }

            // finishing tour
            foreach (Ant ant in this.Ants)
            {
                ant.CloseTour();
                ant.Tours.Length = this.distances.ComputeTourLength(ant.Tours);
            }
        }

        protected virtual void AssignFirstCity()
        {
            foreach (Ant ant in this.Ants)
            {
                int r = this.rnd.Next(this.Cities.Count);
                ant.AddCity(r);
            }
        }

        protected virtual void UpdateStatistics()
        {
            this.iterationCount++;
        }
    }
}
